/*-------------------------------------------------------------------------
 * drawElements Quality Program Platform Utilites
 * ----------------------------------------------
 *
 * Copyright 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Android platform capability query JNI component
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"

#include "tcuCommandLine.hpp"
#include "gluRenderConfig.hpp"
#include "gluRenderContext.hpp"
#include "eglwLibrary.hpp"
#include "eglwEnums.hpp"
#include "egluUtil.hpp"
#include "egluGLUtil.hpp"

#include <jni.h>

namespace
{
namespace opt
{

DE_DECLARE_COMMAND_LINE_OPT(GLMajorVersion, int);
DE_DECLARE_COMMAND_LINE_OPT(GLMinorVersion, int);

} // opt

class GLConfigParser : public tcu::CommandLine
{
public:
					GLConfigParser			(const std::string& argString);

	bool			hasGLMajorVersion		(void) const;
	bool			hasGLMinorVersion		(void) const;
	int				getGLMajorVersion		(void) const;
	int				getGLMinorVersion		(void) const;

private:
	virtual void	registerExtendedOptions	(de::cmdline::Parser& parser);
};

GLConfigParser::GLConfigParser (const std::string& argString)
{
	const std::string execString = "fakebinaryname " + argString; // convert argument list to full command line

	if (!parse(execString))
	{
		tcu::print("failed to parse command line");
		TCU_THROW(Exception, "failed to parse command line");
	}
}

bool GLConfigParser::hasGLMajorVersion (void) const
{
	return getCommandLine().hasOption<opt::GLMajorVersion>();
}

bool GLConfigParser::hasGLMinorVersion (void) const
{
	return getCommandLine().hasOption<opt::GLMinorVersion>();
}

int GLConfigParser::getGLMajorVersion (void) const
{
	DE_ASSERT(hasGLMajorVersion());
	return getCommandLine().getOption<opt::GLMajorVersion>();
}

int GLConfigParser::getGLMinorVersion (void) const
{
	DE_ASSERT(hasGLMinorVersion());
	return getCommandLine().getOption<opt::GLMinorVersion>();
}

void GLConfigParser::registerExtendedOptions (de::cmdline::Parser& parser)
{
	using de::cmdline::Option;

	parser
		<< Option<opt::GLMajorVersion>	(DE_NULL, "deqp-gl-major-version", "OpenGL ES Major version")
		<< Option<opt::GLMinorVersion>	(DE_NULL, "deqp-gl-minor-version", "OpenGL ES Minor version");
}

glu::RenderConfig parseRenderConfig (const std::string& argsStr)
{
	const GLConfigParser parsedCommandLine (argsStr);

	if (!parsedCommandLine.hasGLMajorVersion() ||
		!parsedCommandLine.hasGLMinorVersion())
	{
		tcu::print("minor and major version must be supplied");
		TCU_THROW(Exception, "minor and major version must be supplied");
	}
	else
	{
		const glu::ContextType	testContextType	(glu::ApiType::es(parsedCommandLine.getGLMajorVersion(), parsedCommandLine.getGLMinorVersion()));
		glu::RenderConfig		renderConfig	(testContextType);

		glu::parseRenderConfig(&renderConfig, parsedCommandLine);

		return renderConfig;
	}
}

bool isRenderConfigSupported (const std::string& cmdLineStr)
{
	const glu::RenderConfig		renderConfig	= parseRenderConfig(cmdLineStr);
	const eglw::DefaultLibrary	egl;
	const eglw::EGLDisplay		display			= egl.getDisplay(EGL_DEFAULT_DISPLAY);
	eglw::EGLint				eglMajor		= -1;
	eglw::EGLint				eglMinor		= -1;

	if (display == EGL_NO_DISPLAY)
	{
		tcu::print("could not get default display");
		TCU_THROW(Exception, "could not get default display");
	}

	if (egl.initialize(display, &eglMajor, &eglMinor) != EGL_TRUE)
	{
		tcu::print("failed to initialize egl");
		TCU_THROW(Exception, "failed to initialize egl");
	}
	tcu::print("EGL initialized, major=%d, minor=%d", eglMajor, eglMinor);

	try
	{
		// ignoring return value
		(void)eglu::chooseConfig(egl, display, renderConfig);
	}
	catch (const tcu::NotSupportedError&)
	{
		tcu::print("No matching config");
		egl.terminate(display);
		return false;
	}
	catch (...)
	{
		egl.terminate(display);
		throw;
	}
	egl.terminate(display);

	return true;
}

} // anonymous


DE_BEGIN_EXTERN_C

JNIEXPORT jint JNICALL Java_com_drawelements_deqp_platformutil_DeqpPlatformCapabilityQueryInstrumentation_nativeRenderConfigSupportedQuery (JNIEnv* env, jclass, jstring jCmdLine)
{
	enum
	{
		CONFIGQUERYRESULT_SUPPORTED = 0,
		CONFIGQUERYRESULT_NOT_SUPPORTED = 1,
		CONFIGQUERYRESULT_GENERIC_ERROR = -1,
	};

	std::string			cmdLine;
	const char* const	cmdLineBytes = env->GetStringUTFChars(jCmdLine, DE_NULL);

	if (cmdLineBytes == DE_NULL)
	{
		// no command line is not executable
		tcu::print("no command line supplied");
		return CONFIGQUERYRESULT_GENERIC_ERROR;
	}

	try
	{
		// try to copy to local buffer
		cmdLine = std::string(cmdLineBytes);
	}
	catch (const std::bad_alloc&)
	{
		env->ReleaseStringUTFChars(jCmdLine, cmdLineBytes);
		tcu::print("failed to copy cmdLine");
		return CONFIGQUERYRESULT_GENERIC_ERROR;
	}
	env->ReleaseStringUTFChars(jCmdLine, cmdLineBytes);

	try
	{
		const bool isSupported = isRenderConfigSupported(cmdLine);

		return (isSupported) ? (CONFIGQUERYRESULT_SUPPORTED)
		                     : (CONFIGQUERYRESULT_NOT_SUPPORTED);
	}
	catch (const std::exception& ex)
	{
		// don't bother forwarding the exception to the caller. They cannot do anything with the exception anyway.
		tcu::print("Error: %s", ex.what());
		return CONFIGQUERYRESULT_GENERIC_ERROR;
	}
}

DE_END_EXTERN_C
